package com.learning.spring.aop.aspectj.annotation.function;

import com.learning.spring.aop.aspectj.AopService;
import com.learning.spring.aop.aspectj.AopServiceImpl;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * ClassName: PointcutFunctionAspect
 * Description: 切点函数
 * Date: 2019/5/5 21:31 【需求编号】
 *
 * @author Sam Sho
 * @version V1.0.0
 */
@Aspect
public class PointcutFunctionAspect {


    /**
     * 注解 @annotation 表示标注了某个注解的所有方法
     * LogEntity 注解标注在AopServiceImpl#add()方法上
     */
    @Before("@annotation(com.learning.spring.aop.aspectj.LogEntity)")
    public void before() {
        System.out.println("Hello @Before  @annotation Function");
    }


    /**
     * execution (<修饰符类型>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
     * <p>
     * 大到包，小到入参。
     * <p>
     * 1- 方法签名定义切点
     * execution(public * *(..)) 匹配所有目标类的public 方法：
     * 第一个 * ：返回类型
     * 第二个 * ：方法名
     * 方法中.. ：任意参数
     * <p>
     * execution(* *To(..)) 匹配目标类所有以To为后缀的方法
     * <p>
     * 2- 类定义切点
     * execution(* com.learning.spring.aop.aspectj.AopService.*(..)) 匹配 AopService 接口的所有方法
     * execution(* com.learning.spring.aop.aspectj.AopService+.*(..)) 匹配 AopService 接口及其实现类的所有方法。包括实现类的私有的方法。
     * <p>
     * 3- 类包定义切点 .* 包下所有的类 ..* 包和子孙包
     * execution(* com.learning.spring.aop.aspectj.*(..)) 匹配 com.learning.spring.aop.aspectj 包下所有类所有方法
     * execution(* com.learning.spring.aop.aspectj..*(..)) 匹配 com.learning.spring.aop.aspectj 包下,子孙包下所有类所有方法
     * execution(* com.learning.spring.aop..*.*Service.find*(..)) 匹配 com.learning.spring.aop.aspectj 任意包下Service后缀的类，前缀为find的方法
     * <p>
     * 4- 方法入参定义切点
     * execution(* add(String)) 匹配add(String)方法，且方法参数是String
     */
    @Before("execution(* com.learning.spring.aop.aspectj.AopService+.*(..)) ")
    public void execution() {
        System.out.println("Hello execution Function");
    }


    /**
     * args 函数，接收一个类名，表示目标类方法入参对象是指定类（包含子类）的匹配
     */
    @Before("args(com.learning.spring.aop.aspectj.User)")
    public void args() {
        System.out.println("Hello args Function");
    }


    /**
     * 接收一个注解类的类名。当方法运行时，入参对象标注了指定的注解时，匹配起点。
     */
    @Before("@args(com.learning.spring.aop.aspectj.LogEntity)")
    public void argsAnnotation() {
        System.out.println("Hello @args Function");
    }


    /**
     * within：通过类匹配模式串，针对目标类而言，不是运行期对象的类型。指定的最小范围是类。
     * <p>
     * within(com.learning.spring.aop.aspectj.AopService) 不会匹配任何。AopService 是接口，没有实例。
     * within(com.learning.spring.aop.aspectj.AopServiceImpl) 匹配类方法
     * <p>
     * within(com.learning.spring.aop.*) 匹配com.learning.spring.aop中的所有类，不包括子类
     * within(com.learning.spring.aop..*) 匹配com.learning.spring.aop中的所有类，包括子类
     */
    @Before("within(com.learning.spring.aop.aspectj.AopServiceImpl)")
    public void within() {
        System.out.println("Hello within Function");
    }

    /**
     * 函数 @target(A)：匹配任意标注了@A 的目标类
     */
    @Before("@target(com.learning.spring.aop.aspectj.LogEntity)")
    public void targetAnnotation() {
        System.out.println("Hello @target Function");
    }


    /**
     * 函数 @within(A)：匹配任意标注了@A 的目标类以及子孙类。注意如果是接口标注了@A，实现接口的子类不匹配。
     */
    @Before("@within(com.learning.spring.aop.aspectj.LogEntity)")
    public void withinAnnotation() {
        System.out.println("Hello @within Function");
    }

    /**
     * 函数 target：通过判断目标类是否按类型匹配指定类来决定连接点是否匹配。
     * target(C): 表示如果目标类按类型匹配 C，则目标类的所有方法都匹配切点
     * <p>
     * target(com.learning.spring.aop.aspectj.AopService) AopService 接口其子类的所有方法匹配，包括私有方法（目标类属于AopService类型）
     */
    @Before("target(com.learning.spring.aop.aspectj.AopService)")
    public void target() {

        System.out.println(new AopServiceImpl() instanceof AopService);


        System.out.println("Hello target Function");
    }

    /**
     * 函数 this：通过判断代理对象的类是否按类型匹配指定类来决定是否和切点匹配
     * <p>
     * this(com.learning.spring.aop.aspectj.AopServiceImpl)  不会匹配任何方法。如果不使用CGLib，生成的代理对象属于 AopService 类型。AopService类型 不属于 AopServiceImpl。
     */
    @Before("this(com.learning.spring.aop.aspectj.AopServiceImpl)")
    public void thisFunc() {
        System.out.println("Hello this Function");
    }
}
